class Person(object):
    def __init__(self, name, knows):
        self.name = name
        self.knows = knows
    def __str__(self):
        getName = lambda x: x.name
        k = ', '.join(map(getName, self.knows))
        if k=='': k = '[nobody]'
        return "%s : %s" % (self.name, k)

def stars(people):
    d = {}
    ct = 0
    for p in people:
        for k in p.knows:
            if k in d:
                d[k] += 1 #inc
            else:
                d[k] = 1 #init
        ct += 1
    res = []
    ct /= 2
    for p in d:
        if d[p]>=ct:
            res.append(p)
    return res

def fans(people):
    #a bit dirty :p
    st = stars(people)
    is_star = lambda x: x in st
    for p in people:
        #known stars only
        p.ks = filter(is_star, p.knows)
    is_fan = lambda x: len(x.ks)==len(st)
    ret = filter(is_fan, people)
    for p in people:
        del p.ks #cleanup
    return ret

def fans2(people):
    #a bit cleaner than fans()...
    d = {}
    st = stars(people)
    is_star = lambda x: x in st
    for p in people:
        d[p] = filter(is_star, p.knows)
    is_fan = lambda x: len(d[x])==len(st)
    return filter(is_fan, d)


people = []
people.append(Person('Vivaldi', []))
people.append(Person('Bach', [people[0]]))
people.append(Person('Rameau', [people[0], people[1]])) #correct?
people.append(Person('Guillaume', [people[0], people[1], people[2]]))

print "*** people ***"
for p in people:
    print p

print "\n*** stars ***"
for p in stars(people):
    print p.name

print "\n*** fans ***"
for p in fans(people):
    print p.name

print "\n*** fans2 ***"
for p in fans2(people):
    print p.name
